声明
一个名字(标识符)能够在C++程序里使用之前必须首先声明。也就是说,必须刻画清楚它的类型,以通知编译器这个名字所引用的哪一类的实体。这里有一些例子,展示了各种各样的声明:
char ch;
string s;
int count = 1;
const double pi = 3.1415926535897932385;
extern int error_number;
const char* name = "Njal";
const char* season[] = {"spring","summer","fall","winter"};
struct Date { int d, m, y; };
int day(Data* p) { return p->d; }
double sqrt(double);
template<class T> T abs(T a) { return a < 0 ? -a : a; }
typedef complex<short> Point;
struct User;
enum Beer { Carlsberg, Tuborg, Thor };
namespace NS { int a; }
正如在这些例子中所看到的,声明能做的事情可以比简单地为一个名字关联一个类型更多一些。这些声明中的大部分同时也是定义,也就是说,它们也定义了有关的名字所引用的那个实体。对于ch,它所对应实体就是适当数量的存储,以使它能够被用做一个变量---这块存储被分配。day被定义的东西就是这里所描述的函数。常数pi被定义为具有值3.1415926535897932385。Date被定义为一个新类型。Point被定义为是类型complex< short>,所以Point也就成为complex< short>的同义词。在上面这些声明中,只有
double sqrt(double);
extern int error_number;
struct User;
不是定义,也就是说,它们所引用的实体必须在其他地方定义。函数sqrt的代码(体)必须通过另外的某个声明描述,int变量error_number的存储必须由某个另外的error_number的声明去分配,必须有另外的某个对类型User的声明,定义出这个类型是什么样子。例如,
double sqrt(double d) { /* ... */ }
int error_number = 1;
struct User { /* ... */ };
在一个C++程序里(关于#include的影响,见9.2.3节),每个命名实体必须有恰好一个定义。当然,它们可以有许多声明。一个实体的所有声明必须在所引用的类型上完全一致。所以,下面这个片段就包含两个错误:
int count;
int count; // 错误❌:重复定义
extern int error_number;
extern short error_number; // 错误❌:类型不匹配
下面这个片段则没有错误(有关extern的使用参见9.2节):
extern int error_number;
extern int error_number;
有些定义还为它们所定义的实体确定了一个“值”。例如,
struct Date { int d, m, y; };
typedef complex<short> Point;
int day(Date* p) { return p->d; }
const double pi = 3.1415926535897932385;
对于类型、模版、函数和常数,这种所谓的“值”是持久不变的。而对那些不是常量的数据类型,这种初始值可以在以后改变。例如
void f()
{
int count = 1;
const char* name = "Bjarne"; // name是一个指向常量的变量(5.4.1节)
// ...
count = 2;
name = "Marian";
}
在前面的定义里,只有
char ch;
string s;
没有描述有关的值。对于如何以及何时给变量赋以默认值的问题,请参看4.9.5节和10.4.2节的解释。任何描述了初始值的声明都是一个定义。
🔚